package com.gw.vip.server.service;

import com.gw.vip.common.constants.VipConstants;
import com.gw.vip.common.enums.StatusEnums;
import com.gw.vip.common.exception.BusinessException;
import com.gw.vip.common.exception.ExceptionEnums;
import com.gw.vip.common.exception.ServiceException;
import com.gw.vip.common.util.Snowflake;
import com.gw.vip.server.dto.*;
import com.gw.vip.server.enums.AccTypeEnums;
import com.gw.vip.server.enums.CapitalEnums;
import com.gw.vip.server.enums.CardMakeStatusEnums;
import com.gw.vip.server.enums.TxnTypeEnums;
import com.gw.vip.server.po.*;
import com.gw.vip.server.service.dao_service.*;
import com.gw.vip.server.service.tool_service.PanToolService;
import com.gw.vip.server.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author GW 光武
 * @description
 * @since 2023-03-25
 */
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PanService {

    private final CardMakeService cardMakeService;
    private final PanToolService panToolService;
    private final BsOrderService bsOrderService;
    private final CardInfoService cardInfoService;
    private final AccCoreService accCoreService;
    private final Snowflake snowflake;
    private final UserInfoService userInfoService;
    private final TransService transService;
    private final AccountService accountService;
    private final VoiceService voiceService;
    private final OrderAllService orderAllService;

    @Transactional(rollbackFor = Exception.class)
    public PanResponse panOpen(PanOpenDto dto) {
        if (dto.getPayAmt()==null){
            dto.setPayAmt(BigDecimal.ZERO);
        }
        if (dto.getRebateAmt()==null){
            dto.setRebateAmt(BigDecimal.ZERO);
        }

        CardMake cardMake = cardMakeService.getById(dto.getPan());
        if (cardMake==null){
            log.error("预制卡【{}】信息不存在！", dto.getPan());
            throw new ServiceException(ExceptionEnums.ITEM_NOT_EXIST, "预制卡信息不存在！");
        }

        if (!cardMake.getStatus().equals(CardMakeStatusEnums.ORIG.getKey())){
            log.error("预制卡【{}】无效状态【{}】", cardMake.getId(), cardMake.getStatus());
            throw new ServiceException("预制卡状态无效！");
        }

        BsOrder bsOrder = bsOrderService.getByBase(dto.getShopId(), dto.getClientOrder());
        if (bsOrder!=null){
            log.error("订单已经存在！客户订单号【{}】", dto.getClientOrder());
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "订单已经存在！");
        }

        UserInfo userInfo;
        if (StringUtils.isNotBlank(dto.getUserId())){
            userInfo = userInfoService.getById(dto.getUserId());;
            if (userInfo==null){
                log.error("用户【{}】不存在！", dto.getUserId());
                throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "用户不存在！");
            }
        }

        bsOrder = panToolService.init(dto);
        bsOrderService.save(bsOrder);

        cardMake.setStatus(CardMakeStatusEnums.SALE.getKey());
        cardMake.setUpdTime(LocalDateTime.now());
        cardMakeService.updateById(cardMake);

        //生成卡信息
        CardInfo cardInfo = panToolService.initCardInfo(cardMake);
        cardInfo.setUserId(dto.getUserId());
        cardInfoService.save(cardInfo);

        List<Trans> transList = new ArrayList<>();

        //储值
        if (dto.getPayAmt().compareTo(BigDecimal.ZERO)>0){
            AccOpenReq accOpenReq = new AccOpenReq();
            accOpenReq.setClientTransId(snowflake.nextStringId());
            accOpenReq.setOrderId(bsOrder.getId());
            accOpenReq.setCapitalId(CapitalEnums.CASH.getKey());
            accOpenReq.setAccType(AccTypeEnums.MAIN.getKey());
            accOpenReq.setUserId(dto.getUserId());
            accOpenReq.setPan(cardInfo.getId());
            accOpenReq.setTxnAmt(dto.getPayAmt());
            accOpenReq.setTxnDateTime(bsOrder.getCrtTime());
            accOpenReq.setCrtName(VipConstants.SYS);
            AccountResponse accountResponse = accCoreService.accOpen(accOpenReq);

            Trans trans = transService.getById(accountResponse.getId());
            transList.add(trans);
        }

        //返利
        if (dto.getRebateAmt().compareTo(BigDecimal.ZERO)>0){
            //开通帐户
            AccOpenReq accOpenReq = new AccOpenReq();
            accOpenReq.setClientTransId(snowflake.nextStringId());
            accOpenReq.setOrderId(bsOrder.getId());
            accOpenReq.setCapitalId(CapitalEnums.REBATE.getKey());
            accOpenReq.setAccType(AccTypeEnums.MAIN.getKey());
            accOpenReq.setUserId(dto.getUserId());
            accOpenReq.setPan(cardInfo.getId());
            accOpenReq.setTxnAmt(dto.getRebateAmt());
            accOpenReq.setTxnDateTime(bsOrder.getCrtTime());
            accOpenReq.setCrtName(VipConstants.SYS);
            AccountResponse accountResponse = accCoreService.accOpen(accOpenReq);

            Trans trans = transService.getById(accountResponse.getId());
            transList.add(trans);
        }

        BigDecimal balAmt = BigDecimal.ZERO;
        for (Trans trans:transList){
            balAmt = balAmt.add(trans.getBalAmt());
        }

        //更新交易
        bsOrder.setBalAmt(balAmt);
        bsOrder.setStatus(StatusEnums.SUCCESS.getCode());
        bsOrderService.updateById(bsOrder);

        PanResponse vo = new PanResponse();
        vo.setOrderId(bsOrder.getId());
        vo.setBalAmt(balAmt);
        vo.setStatus(bsOrder.getStatus());
        vo.setTransList(transList);
        return vo;
    }

    /**
     * 消费
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public PanResponse panSale(PanSaleDto dto) {
        if (dto.getTxnAmt().compareTo(BigDecimal.ZERO)<=0){
            log.error("消费金额要大于0");
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "消费金额要大于0!");
        }

        //订单重复检查
        if (bsOrderService.isExist(dto.getShopId(), dto.getClientOrder())) {
            throw new ServiceException(ExceptionEnums.ITEM_IS_EXIST);
        }

        CardInfo cardInfo = cardInfoService.getById(dto.getPan());
        if (cardInfo==null){
            log.error("卡【{}】不存在！", dto.getPan());
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "卡号不存在！");
        }

        BsOrder bsOrder = panToolService.initTrans(dto, cardInfo);
        bsOrderService.save(bsOrder);

        //查找可以扣款帐户
        List<Account> accountList = accountService.avaAccByPan(cardInfo.getId());

        //扣款帐户排序
        panToolService.sortAcc(accountList);

        //帐户扣款
        LocalDateTime txnDateTime = bsOrder.getCompleteTime();
        //剩余扣款金额
        BigDecimal nextTxnAmt = bsOrder.getTxnAmt();
        List<Trans> transList = new ArrayList<>();
        for (Account account : accountList) {
            //此帐户最大可扣金额
            BigDecimal maxAmt = account.getAmt().subtract(account.getLockAmt());
            if (maxAmt.compareTo(nextTxnAmt) >= 0) {
                //可用余额够扣
                AccSaleReq accSaleReq = new AccSaleReq();
                accSaleReq.setClientTransId(snowflake.nextStringId());
                accSaleReq.setAccount(account.getId());
                accSaleReq.setOrderId(bsOrder.getId());
                accSaleReq.setTxnAmt(nextTxnAmt);
                accSaleReq.setTxnDateTime(txnDateTime);
                AccountResponse accountResponse = accCoreService.accSale(accSaleReq);
                Trans trans = new Trans();
                BeanUtils.copyProperties(accountResponse, trans);
                transList.add(trans);
                nextTxnAmt = BigDecimal.ZERO;
            } else {
                //可用余额不够扣
                AccSaleReq accSaleReq = new AccSaleReq();
                accSaleReq.setClientTransId(snowflake.nextStringId());
                accSaleReq.setAccount(account.getId());
                accSaleReq.setOrderId(bsOrder.getId());
                accSaleReq.setTxnAmt(maxAmt);
                accSaleReq.setTxnDateTime(txnDateTime);
                AccountResponse accountResponse = accCoreService.accSale(accSaleReq);
                Trans trans = new Trans();
                BeanUtils.copyProperties(accountResponse, trans);
                transList.add(trans);
                nextTxnAmt = nextTxnAmt.subtract(maxAmt);
            }
            if (nextTxnAmt.compareTo(BigDecimal.ZERO) == 0) {
                //扣款完成
                break;
            }
        }
        if (nextTxnAmt.compareTo(BigDecimal.ZERO) > 0) {
            //总可用余额不够扣
            log.warn("卡号【{}】总可用余额不够扣, 交易金额【{}】, 还需扣款【{}】", cardInfo.getId(), bsOrder.getTxnAmt(), nextTxnAmt);
            throw new ServiceException(ExceptionEnums.LOW_AMT);
        }

        BigDecimal payAmt = transList.stream().filter(transAcc -> transAcc.getCapitalId().equals(CapitalEnums.CASH.getKey())).map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal rebateAmt = transList.stream().filter(accountTrans -> accountTrans.getCapitalId().equals(CapitalEnums.REBATE.getKey())).map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);

        //获取余额
        BigDecimal balAmt = accountService.getAvaBalByPan(cardInfo.getId());

        //更新trans信息
        bsOrder.setPayAmt(payAmt);
        bsOrder.setRebateAmt(rebateAmt);
        bsOrder.setBalAmt(balAmt);
        bsOrder.setStatus(StatusEnums.SUCCESS.getCode());
        bsOrderService.updateById(bsOrder);

        voiceService.broadcastBalance(balAmt);

        PanResponse vo = new PanResponse();
        vo.setOrderId(bsOrder.getId());
        vo.setBalAmt(balAmt);
        vo.setStatus(bsOrder.getStatus());
        vo.setTransList(transList);
        return vo;
    }

    /**
     * 退款
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public PanResponse panRefund(PanRefundDto dto) {
        BsOrder srcBsOrder = orderAllService.getByBase(dto.getShopId(), dto.getSrcClientOrder());
        if (srcBsOrder ==null){
            log.error("原交易订单不存在！shopId【{}】，客户订单号【{}】", dto.getShopId(), dto.getSrcClientOrder());
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "原交易订单不存在!");
        }

        if (!srcBsOrder.getOrderType().equals(TxnTypeEnums.SALE.getKey())){
            log.error("原订单【{}】不是消费订单！", srcBsOrder.getId());
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "原订单不是消费订单");
        }

        if (srcBsOrder.getRefundAmt().compareTo(BigDecimal.ZERO)>0){
            log.error("原订单【{}】已经退款，不能重复退款！", srcBsOrder.getId());
            throw new ServiceException(ExceptionEnums.BUSINESS_FORBIDDEN, "原订单已经退款，不能重复退款！");
        }

        if (!dto.getShopId().equals(srcBsOrder.getShopId())){
            log.error("店铺编号不一致！上送店铺【{}】，原交易店铺【{}】", dto.getShopId(), srcBsOrder.getShopId());
            throw new ServiceException(ExceptionEnums.BUSINESS_FORBIDDEN, "店铺编号不一致！");
        }

        if (dto.getTxnAmt().compareTo(BigDecimal.ZERO)<=0){
            log.error("退款金额要大于0");
            throw new ServiceException(ExceptionEnums.ERROR_PARAMS, "退款金额要大于0");
        }

        //订单重复检查
        if (bsOrderService.isExist(dto.getShopId(), dto.getClientOrder())) {
            log.error("订单已经存在。客户订单号【{}】", dto.getClientOrder());
            throw new ServiceException(ExceptionEnums.ITEM_IS_EXIST);
        }
        BsOrder bsOrder = panToolService.initTrans(srcBsOrder, dto);
        bsOrderService.save(bsOrder);

        CardInfo cardInfo = cardInfoService.getById(srcBsOrder.getPan());
        if (cardInfo==null){
            log.error("卡号【{}】不存在！", srcBsOrder.getPan());
            throw new ServiceException("卡号不存在！");
        }

        //找到原交易明细
        List<Trans> srcTransList = transService.listByOrderId(srcBsOrder.getId());

        //金额检查
        BigDecimal srcTxnAmt = srcTransList.stream().map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (bsOrder.getTxnAmt().compareTo(srcTxnAmt) != 0) {
            log.error("原交易金额和退款金额不一致。原交易总额【{}】, 退款金额【{}】", srcTxnAmt, bsOrder.getTxnAmt());
            throw new ServiceException("金额不一致！");
        }

        //各明细退款
        List<Trans> transList = new ArrayList<>();
        LocalDateTime txnDateTime = bsOrder.getCrtTime();
        for (Trans srcTrans :srcTransList){
            Account account = accountService.getById(srcTrans.getAccount());
            if (account == null){
                log.error("帐户【{}】不存在！", srcTrans.getAccount());
                throw new ServiceException("帐户不存在！");
            }

            AccRefundReq accRefundReq = new AccRefundReq();
            accRefundReq.setClientTransId(snowflake.nextStringId());
            accRefundReq.setSrcClientTransId(srcTrans.getClientTransId());
            accRefundReq.setAccount(account.getId());
            accRefundReq.setOrderId(bsOrder.getId());
            accRefundReq.setTxnAmt(srcTrans.getTxnAmt());
            accRefundReq.setTxnDateTime(txnDateTime);
            AccountResponse accountResponse = accCoreService.accRefund(accRefundReq);
            Trans trans = new Trans();
            BeanUtils.copyProperties(accountResponse, trans);
            transList.add(trans);
        }

        BigDecimal payAmt = transList.stream().filter(transAcc -> transAcc.getCapitalId().equals(CapitalEnums.CASH.getKey())).map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal rebateAmt = transList.stream().filter(accountTrans -> accountTrans.getCapitalId().equals(CapitalEnums.REBATE.getKey())).map(Trans::getTxnAmt).reduce(BigDecimal.ZERO, BigDecimal::add);

        //获取余额
        BigDecimal balAmt = accountService.getAvaBalByPan(cardInfo.getId());

        //更新原交易
        srcBsOrder.setRefundAmt(srcBsOrder.getRefundAmt().add(bsOrder.getTxnAmt()));
        srcBsOrder.setUpdTime(txnDateTime);
        orderAllService.updateById(srcBsOrder);

        //更新订单信息
        bsOrder.setPayAmt(payAmt);
        bsOrder.setRebateAmt(rebateAmt);
        bsOrder.setBalAmt(balAmt);
        bsOrder.setStatus(StatusEnums.SUCCESS.getCode());
        bsOrderService.updateById(bsOrder);

        //播报余额
        voiceService.broadcastBalance(balAmt);

        PanResponse vo = new PanResponse();
        vo.setOrderId(bsOrder.getId());
        vo.setBalAmt(bsOrder.getBalAmt());
        vo.setStatus(bsOrder.getStatus());
        vo.setTransList(transList);
        return vo;
    }

    public PanAmtQueryVo amtQuery(PanAmtQueryDto dto) {
        CardInfo cardInfo = cardInfoService.getById(dto.getPan());
        if (cardInfo==null){
            log.error("卡【{}】不存在！", dto.getPan());
            throw new BusinessException(ExceptionEnums.ITEM_NOT_EXIST, "卡不存在！");
        }

        //获取余额
        BigDecimal balAmt = accountService.getAvaBalByPan(cardInfo.getId());

        //播报余额
        voiceService.broadcastBalance(balAmt);

        PanAmtQueryVo vo = new PanAmtQueryVo();
        vo.setAvaAmt(balAmt);
        return vo;
    }
}
